package com.cy.detectsys.controller.detect;

import cn.hutool.core.io.FileUtil;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.cy.detectsys.config.CustomException;
import com.cy.detectsys.constant.MinioProperties;
import com.cy.detectsys.constant.RSAKeyConstant;
import com.cy.detectsys.entity.*;
import com.cy.detectsys.mapper.DbPortscanMapper;
import com.cy.detectsys.mapper.DbTargetSubdomainMapper;
import com.cy.detectsys.mapper.DbWidePingMapper;
import com.cy.detectsys.model.req.PackageReq;
import com.cy.detectsys.mapper.MinioFileMapper;
import com.cy.detectsys.model.OssFile;
import com.cy.detectsys.model.packageModel.NodeJsonForm;
import com.cy.detectsys.service.*;
import com.cy.detectsys.util.MinioTemplate;
import com.cy.detectsys.util.R;
import com.cy.detectsys.util.TaskIdGenerator;
import io.minio.MinioClient;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import net.lingala.zip4j.exception.ZipException;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

import static com.cy.detectsys.constant.LocalPathContant.BATH_PATH;
import static com.cy.detectsys.constant.LocalPathContant.PROBE_PATH;
import static com.cy.detectsys.constant.PackagePasswordConstant.PASSWORD;
import static com.cy.detectsys.util.FileOperateUtil.moveFile;
import static com.cy.detectsys.util.ZipUtil.zipFolder;


@RestController
@Slf4j
@RequestMapping("task")
public class GeneratePackageController {



//    public static final String BASE_PATH = "D:\\\\36\\\\cetc-api\\\\src\\\\main\\\\resources\\\\swap";
//    public static final String PROBE_PATH = "D:\\36\\cetc-api\\src\\main\\resources\\probe";


    @Resource
    private TaskIdGenerator taskIdGenerator;

    @Resource
    private ProbenodeResourceService probenodeResourceService;

    @Resource
    private TcPackageService tcPackageService;

    @Resource
    private TcTaskService tcTaskService;


    @Resource
    private MinioFileMapper minioFileMapper;

    @Resource
    private MinioProperties ossProperties;

    @Resource
    private MinioClient client;

    /**
     * 字符集
     */
    private static final String ENCODING = "UTF-8";

    @RequestMapping("package")
    @Transactional
    public void detect(@RequestBody PackageReq req, HttpServletResponse response) throws IOException, ZipException {

        Integer packageLevel = req.getPackageLevel();

        if (req.getDomainList() == null) {
            throw new CustomException("域名列表不能为空");
        }

        if (packageLevel == null) {
            throw new CustomException("任务阶段没指定");
        }

        String password = req.getPassword();

        if (StrUtil.isEmpty(password)) {
            password = PASSWORD;
        }

        List<String> domainList = req.getDomainList();
        List<String> probeIdList = req.getProbeIdList();

        String taskNum = taskIdGenerator.generateRWTaskId();
        TcPackage tcPackage = new TcPackage();
        tcPackage.setPackageNum(taskNum);
        tcPackage.setCreateTime(new Date());
        tcPackage.setCreateUser("管理员");
        tcPackage.setLevel(packageLevel);
        tcPackage.setMaxLevel(req.getMaxLevel());
        tcPackage.setStatus("任务执行中");
        tcPackage.setPassword(password);
        tcPackage.setDomains(JSON.toJSONString(domainList));

        tcPackageService.save(tcPackage);  //存入数据库


        //创建 base配置包
        String basePackagePath = BATH_PATH + File.separator + taskNum;
        File basePackage = new File(basePackagePath);
        if (!basePackage.exists()) {
            basePackage.mkdirs(); // 创建文件夹
        }

        List<NodeJsonForm> nodeInfoList = new ArrayList<>();

        //根据探测的探针开展探测任务

        for (String probeID : probeIdList) {

            String tcTaskNum = taskIdGenerator.generateTCTaskId();
            String tcTaskFolderPath = basePackagePath + File.separator + tcTaskNum;
            File tcTaskFolder = new File(tcTaskFolderPath);
            ProbeResource probeResource = getOneProbe(probeID);  //获取本次探测任务需要的探针

            if (probeResource == null) {
                throw new CustomException("探测文件不存在");
            }

            if (!tcTaskFolder.exists()) {
                tcTaskFolder.mkdirs(); // 创建文件夹
            }

            //从minio上下载文件本地
            if (tcTaskFolder.isDirectory()) {

                //创建任务实体类
                TcTask tcTask = new TcTask();
                tcTask.setTcTackNum(tcTaskNum);
                tcTask.setParam(JSON.toJSONString(domainList));
                tcTask.setPackageId(tcPackage.getId());
                tcTask.setProbe(probeResource.getProbeName());
                tcTask.setCreateUser("管理员");
                tcTask.setStatus("执行中");
                tcTaskService.save(tcTask);


                //获取探测文件
                generateProbePyFile(probeResource, tcTaskFolderPath);
                //生成RSA密钥文件
                generateRSAFile(tcTaskFolderPath);
                //生成proxynode.json文件
                generateProxynodeFile(tcTaskFolderPath, tcTask);
                //生成target.json文件   服了，需求天天改，什么结构能跟的上，操！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！
                String scriptName = generateTargetJsonFile(tcTaskFolderPath, tcTask, probeResource,req);

                zipFolder(tcTaskFolderPath, true, password); // 压缩内层探测任务包

                /**
                 * 准备NodoInfo的资料
                 */
                NodeJsonForm nodeJsonForm = new NodeJsonForm();
                nodeJsonForm.setTask_id(tcTask.getId());
                nodeJsonForm.setScript_name(scriptName);
                nodeJsonForm.setTask_num(tcTask.getTcTackNum());
                ProbenodeResource oneProbeNode = probenodeResourceService.getOneProbeNode();
                nodeJsonForm.setTask_probenode_ip(oneProbeNode.getTaskProbenodeIp());
                nodeJsonForm.setTask_probenode_port(oneProbeNode.getTaskProbenodePort());
                nodeJsonForm.setTask_probenode_user(oneProbeNode.getTaskProbenodeUser());
                nodeJsonForm.setTask_probenode_password(oneProbeNode.getTaskProbenodePassword());
                nodeInfoList.add(nodeJsonForm);
                tcTask.setProbenodeName(oneProbeNode.getTaskProbenodeIp());
                tcTaskService.updateById(tcTask);
            }
        }

        //在文件包 新建  nodeinfo.json 文件
        String nodeInfoFilePath = basePackagePath + File.separator + "NodeInfo.json";
        File nodeInfoFile = new File(nodeInfoFilePath);
        if (!nodeInfoFile.exists()) {

            FileUtil.touch(nodeInfoFile); // 创建nodeinfo.json

            HashMap<String, Object> nodeInfoMap = new HashMap<>();
            nodeInfoMap.put("containers", nodeInfoList);

            String nodeinfoContent = JSON.toJSONString(nodeInfoMap);

            FileUtil.writeUtf8String(nodeinfoContent, nodeInfoFile);
            log.info("nodeinfo文件写入成功");

        }
        zipFolder(basePackagePath, true, password); //  压缩最外层配置文件包

        //最后一步将配置文件包上传minio
        File packageZipFile = new File(basePackagePath + ".zip");

        OssFile ossFile = minioTemplate.upLoadFile("TCpackage", packageZipFile);

        MinioFile minioFile = new MinioFile();
        minioFile.setFilePath(ossFile.getFilePath());
        minioFile.setFileName(ossFile.getOriginalName());
        minioFile.setCreateTime(new Date());
        minioFile.setPackageId(tcPackage.getId());
        minioFileMapper.insert(minioFile);
        tcPackage.setPackagePath(ossFile.getFilePath());
        tcPackageService.updateById(tcPackage);
        //将压缩文件放入response
        FileInputStream fileInputStream = null;
        try {
            fileInputStream = new FileInputStream(packageZipFile);
            // 设置文件ContentType类型，这样设置，会自动判断下载文件类型
            response.setContentType("application/x-msdownload");
            response.setCharacterEncoding(ENCODING);
            // 设置文件头：最后一个参数是设置下载的文件名并编码为UTF-8
            response.setHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(ossFile.getOriginalName(), ENCODING));
            IoUtil.copy(fileInputStream, response.getOutputStream());
        } catch (Exception e) {
            log.error("Exception:{}", e);
        } finally {
            IoUtil.close(fileInputStream);
        }

        //删除本地zip文件
        packageZipFile.delete();
    }


    @Resource
    private DbTargetSubdomainMapper targetSubdomainMapper;

    @Resource
    private DbWidePingMapper widePingMapper;
    @Resource
    private DbPortscanMapper portscanMapper;

    private String generateTargetJsonFile(String tcTaskFolderPath, TcTask tcTask, ProbeResource probeResource, PackageReq req) {

        String proxynodeFile = tcTaskFolderPath + File.separator + "target.json";
        HashMap<String, Object> targetMap = new HashMap<>();
        String probenodeName = tcTask.getProbe() + tcTask.getId();
        HashMap<String, List> params = new HashMap<>();
        List<String> domains = new ArrayList<>();
        List<String> subdomains = new ArrayList<>();
        List<String> ips = new ArrayList<>();
        List<String> ports = new ArrayList<>();
        List<DbPortscan> portscans = new ArrayList<>();


        switch (probeResource.getProbeInput()) {

            case "subdomain":
                //根据 域名 找子域名
                domains = JSON.parseArray(tcTask.getParam(), String.class);
                subdomains = targetSubdomainMapper.selectSubdomains(domains);
                if (subdomains != null&& subdomains.size()>0) {

                    tcTask.setParam(JSON.toJSONString(subdomains));
                    params.put("param", subdomains);
                }else {
                    throw new CustomException("subdomain查询为空");
                }

                break;
            case "ip":
                //根据 域名 找子域名 再去 wideping 找 ip
                domains = JSON.parseArray(tcTask.getParam(), String.class);
                subdomains = targetSubdomainMapper.selectSubdomainsAlive(domains); //查询存活子域名
                ips = widePingMapper.selectIps(subdomains);
                if (ips != null && ips.size()>0) {
                    tcTask.setParam(JSON.toJSONString(ips));
                    params.put("param", ips);
                    break;
                }else {
                    throw new CustomException("ip查询为空");
                }

            case "port":
                domains = JSON.parseArray(tcTask.getParam(), String.class);
                subdomains = targetSubdomainMapper.selectSubdomainsAlive(domains); //查询存活子域名
                ips = widePingMapper.selectIps(subdomains);
                portscans = portscanMapper.selectIps(ips);
                for (DbPortscan portscan : portscans) {
                    String kyEdPortscanIp = portscan.getKyEdPortscanIp();
                    String kyEdPortscanServer = portscan.getKyEdPortscanServer();
                    Integer kyEdPortscanPort = portscan.getKyEdPortscanPort();

                    String param = kyEdPortscanIp + ":" + kyEdPortscanServer + ":" + kyEdPortscanPort;
                    ports.add(param);
                }
                tcTask.setParam(JSON.toJSONString(ports));
                params.put("param", ports);
                break;

            case "domain_ip":
                List<String> param = new ArrayList<>();
                for (String domain : req.getDomainList()) {
                    StringBuilder domainparams = new StringBuilder();
                    domainparams.append("domain");
                    domainparams.append("=\"");
                    domainparams.append(domain + "\"");
                    param.add(domainparams.toString());
                }
                for (String ip : req.getIpList()) {
                    StringBuilder domainparams = new StringBuilder();
                    domainparams.append("ip");
                    domainparams.append("=\"");
                    domainparams.append(ip + "\"");
                    param.add(domainparams.toString());
                }
                tcTask.setParam(JSON.toJSONString(param));
                params.put("param", param );
                break;

            default:
                params.put("param", JSON.parseArray(tcTask.getParam()));
        }
        targetMap.put(probenodeName, params);

        FileUtil.writeUtf8String(JSON.toJSONString(targetMap), proxynodeFile);
        return probenodeName;
    }

    private void generateProbePyFile(ProbeResource probeResource, String tcTaskFolderPath) throws IOException {
        File probeFile = getProbeFile(probeResource.getProbePath());
        //转移探测文件到当前目录
        moveFile(probeFile, tcTaskFolderPath);


        //转移探测 额外资源文件到当前目录
        if (StrUtil.isNotEmpty(probeResource.getProbeExtResource())) {
            File probeExtResource = getProbeFile(probeResource.getProbeExtResource());
            moveFile(probeExtResource, tcTaskFolderPath);
        }

    }


    @Resource
    private ProxynodeResourceService proxynodeResourceService;

    //生成proxynode.json文件
    private void generateProxynodeFile(String tcTaskFolderPath, TcTask tcTask) {
        String proxynodeFile = tcTaskFolderPath + File.separator + "proxynode.json";
        ProxynodeResource oneProxyNode = proxynodeResourceService.getOneProxyNode();
        FileUtil.writeUtf8String(JSON.toJSONString(oneProxyNode), proxynodeFile);
        tcTask.setProxynodeName(oneProxyNode.getTaskProxynodeIpIn());
    }


    @Resource
    private ProbeResourceService probeResourceService;

    //拿到探测节点
    private ProbeResource getOneProbe(String probeID) {

        QueryWrapper<ProbeResource> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", probeID);
        queryWrapper.last("ORDER BY RAND()");
        ProbeResource one = probeResourceService.getOne(queryWrapper);
        return one;
    }


    //生成RSA公钥文件
    private void generateRSAFile(String tcTaskFolderPath) {
        String filePath = tcTaskFolderPath + File.separator + "publickey.rsa";
        FileUtil.touch(filePath);

        String key = "-----BEGIN PUBLIC KEY----- \r\n" + RSAKeyConstant.publicKey + "\r\n-----END PUBLIC KEY-----";

        FileUtil.writeUtf8String(key, filePath);
    }

    @Resource
    private MinioTemplate minioTemplate;

    //拿到探针文件
    private File getProbeFile(String probeFile) {

        //boolean b = fileExists(PROBE_PATH, "xxx");  //本来想在本地找的文件现在感觉没必要
        //每一次都去下载新的probe文件，确保每次文件都是最新的
        //String probeFilePath = minioTemplate.downloadFileFromMinio("/配色统一格式.docx", PROBE_PATH + File.separator + "/配色统一格式.docx");
        File file = new File(probeFile);
        String probeFilePath = minioTemplate.downloadFileFromMinio(probeFile, PROBE_PATH + File.separator + file.getName());
        return new File(probeFilePath);
    }


    /**
     * 删除任务配置包数据
     *
     * @param id
     * @return
     */
    @RequestMapping("delete")
    public R delete(@RequestParam Integer id) {
        return R.ok(tcPackageService.removeById(id));
    }


}
